/*
 * Residue.cpp
 *
 *  Created on: Jul 6, 2013
 *      Author: x
 */
#include <vector>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include "Residue.h"
#include "Conformer.h"
#include "Protein.h"

using namespace std;

namespace mcce {

Residue::Residue(): Fixed(false), inProt(0), onFreeConf(0) {}

Residue::Residue(const Conformer &conf):Fixed(false), inProt(0), onFreeConf(0) {
	resName = conf.getResName();
	conformers.push_back(conf);
}

Residue::~Residue() {
	// TODO Auto-generated destructor stub
}

Residue& Residue::addConformer(const Conformer &conf) {
	conformers.push_back(conf);
	return *this;
}

// Adjust the flag of conformers in the residue, and divide them into groups by conformer type.
void Residue::adjustHead3Flag() {
	int n_free = 0;
	float fixed_hocc = 0.0;

	for (vector<Conformer>::iterator confIt=conformers.begin(); confIt != conformers.end(); confIt++) {
		if (confIt->getHFlag() == 'f') {
			confIt->setOcc(0.0);
			n_free++;
		}
		else if (confIt->getHFlag() == 't') {
			fixed_hocc += confIt->getOcc();
		}
	}

	if (n_free == 1) {
		for (vector<Conformer>::iterator confIt=conformers.begin(); confIt != conformers.end(); confIt++) {
			if (confIt->getHFlag() == 'f') {
				confIt->setHFlag('t');
				float left_hocc = 1 - fixed_hocc;
				if (left_hocc < 0) left_hocc = 0.0;
				if (left_hocc > 1.0) left_hocc = 1.0;
				confIt->setOcc(left_hocc);
			}
		}
		Fixed = true;
	}
	else if (n_free > 1) {
		for (vector<Conformer>::iterator confIt=conformers.begin(); confIt != conformers.end(); confIt++) {
			if (confIt->getHFlag() == 't' && confIt->getOcc() > 0.001) {
				confIt->setOcc(0.0);    // occ of all the fixed conformers in a free residue have to be 0.
			}
		}
	}
	else if (n_free == 0) {
		// Have to make sure the occ of all fixed conformers add up to 1.
		Fixed = true;
	}

	// group all the conformers in the residue
	//setGroupedFreeConf();
}

bool Residue::isResNeighbor(Residue &res, float threshold) {
	// Don't include the residue itself.
	if (this == &res) return false;

	bool isNeighbor = false;
	for (vector<Conformer>::iterator it = conformers.begin(); it != conformers.end(); it++) {
		if (it->getHFlag() == 't' && it->getOcc() < 0.01) continue;
		for (vector<Conformer>::iterator secondIt=res.getConformers().begin(); secondIt != res.getConformers().end(); secondIt++) {
			if (secondIt->getHFlag() == 't' && secondIt->getOcc() < 0.01) continue;

			if (fabs(it->getPairwise()[secondIt->getUniqId()].sumPairwise) > threshold) {
				isNeighbor = true;
				break;
			}
		}
		if (isNeighbor) break;
	}

	return isNeighbor;
}

// group all the "free" conformers
void Residue::setGroupedFreeConf() {
	groupedFreeConf.clear();

	for (vector<Conformer>::iterator confIt=conformers.begin(); confIt != conformers.end(); confIt++) {
		if (confIt->getHFlag() == 't') continue;

		bool foundSameType = false;

		//
		for (vector<vector<Conformer*> >::iterator firstIt=groupedFreeConf.begin(); firstIt != groupedFreeConf.end(); firstIt++) {
			vector<Conformer*>::iterator secondIt = firstIt->begin();
			if (secondIt != firstIt->end() && confIt->isSameConfType(*(*secondIt))) {
				foundSameType = true;
				firstIt->push_back(&(*confIt));
				break;
			}
		}

		if (!foundSameType) {
			vector<Conformer*> newType;
			newType.push_back(&(*confIt));
			groupedFreeConf.push_back(newType);
		}
	}
}

Conformer* Residue::nextDiffConf(Conformer *conf) {
	Conformer* NextConf = 0;

	if (groupedFreeConf.size() == 1) {
		// if there is only one type of conformers, there has to be at least two conformers of that type.
		while(true) {
			vector<Conformer*>::iterator confIt=groupedFreeConf.begin()->begin();
			advance(confIt, rand() % groupedFreeConf.begin()->size());
			if ((*confIt) != conf) {
				NextConf = *confIt;
				break;
			}
		}
	}
	else if (groupedFreeConf.size() > 1) {
		vector<vector<Conformer*> >::iterator groupIt = groupedFreeConf.begin();
		advance(groupIt, rand() % groupedFreeConf.size());

		if (groupIt->size() == 1) {
			NextConf = *(groupIt->begin());
		}
		else if (groupIt->size() > 1) {
			while(true) {
				vector<Conformer*>::iterator confIt=groupIt->begin();
				advance(confIt, rand() % groupIt->size());
				if ((*confIt) != conf) {
					NextConf = *confIt;
					break;
				}
			}
		}
	}

	return NextConf;
}



void Residue::setFreeConformers() {
	freeConformers.clear();
	for (vector<Conformer>::iterator confIt=conformers.begin(); confIt != conformers.end(); confIt++) {
		if (confIt->getHFlag() == 'f') {
			freeConformers.push_back(&(*confIt));
		}
	}
}

void Residue::setNeighborRes(float threshold) {
	neighborRes.clear();
//	cout << "threshold " << threshold << endl;
	for (vector<Residue>::iterator resIt=inProt->getResidues().begin(); resIt != inProt->getResidues().end(); resIt++) {
		if (this == &(*resIt)) continue;
		if (isResNeighbor((*resIt), threshold)) {
			neighborRes.push_back(&(*resIt));
		}
	}
}

void Residue::setFreeNeighborRes() {
	freeNeighborRes.clear();
	for (vector<Residue*>::iterator resIt=neighborRes.begin(); resIt != neighborRes.end(); resIt++) {
		if ((*resIt)->isFixed()) continue;
		freeNeighborRes.push_back(*resIt);
	}
	/*cout << resName << " free Neighbor size: " << freeNeighborRes.size() << endl;
	for(size_t iz=0; iz<freeNeighborRes.size(); iz++) {
		cout << freeNeighborRes[iz]->getResName() << endl;
	}*/
}
} /* namespace mcce */
